package org.loadsavant.task;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.loadsavant.task.parse.TaskDefinitionParser;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;


/**
 * A default implementation of {@link TaskLoader} which locates candidate 
 * classes using a {@link ClassLoader}.  The resources referenced by the
 * class loader must exist on the local file system as files in a JAR.
 * 
 * @author Matt Betzel mattbetzel@gmail.com
 */
public class TaskLoaderImpl implements TaskLoader {
	private static final Log log = LogFactory.getLog(TaskLoaderImpl.class);
	
	private static final String CLASS_POSTFIX = "/**/*.class";
	
	private String searchPath;
	private ClassLoader classLoader;
	private TaskDefinitionParser parser;
	
	/**
	 * Create a TaskLoaderImpl which locates task classes from the
	 * specified {@link ClassLoader} in the package specified by 
	 * packagePrefix or any of its subpackages.  All classes are 
	 * loaded using the specified {@link ClassLoader} and 
	 * definitions created with the {@link TaskDefinitionParser}.
	 * 
	 * @param packagePrefix package used as the root of the search
	 * @param classLoader class loader used as the task candidate space
	 * and to load the valid tasks.
	 * @param parser {@link TaskDefinitionParser} used to validate and
	 * parse classes into task definitions.
	 */
	public TaskLoaderImpl(String packagePrefix, ClassLoader classLoader, TaskDefinitionParser parser) {
		this.searchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + 
		ClassUtils.convertClassNameToResourcePath(packagePrefix) + CLASS_POSTFIX;
		this.classLoader = classLoader;
		this.parser = parser;
	}
	
	/**
	 * Create a TaskLoaderImpl which locates task classes from the
	 * specified {@link ClassLoader} in any package.  All classes are 
	 * loaded using the specified {@link ClassLoader} and 
	 * definitions created with the {@link TaskDefinitionParser}.
	 * 
	 * @param classLoader class loader used as the task candidate space
	 * and to load the valid tasks.
	 * @param parser {@link TaskDefinitionParser} used to validate and
	 * parse classes into task definitions.
	 */
	public TaskLoaderImpl(ClassLoader classLoader, TaskDefinitionParser parser) {
		this("", classLoader, parser);
	}
	
	/** {@inheritDoc} */
	public Set<TaskDefinition> getTaskDefinitions() {
		ResourcePatternResolver resourceResolver = 
			new PathMatchingResourcePatternResolver(classLoader);
		
		Set<TaskDefinition> definitions = new HashSet<TaskDefinition>();	
		
		try {
			Resource[] resources = resourceResolver.getResources(searchPath);
			
			for (Resource resource : resources) {
				if (resource.isReadable()) {					
					TaskDefinition definition = 
						parser.createTaskDefinition(resource.getInputStream());
					
					if (definition != null) {
						definitions.add(definition);
					}
				}
			}
		} catch (IOException e) {
			log.error("Unable to retrieve tasks from classpath", e);
		}		
		
		return Collections.unmodifiableSet(definitions);
	}	

	/** {@inheritDoc} */
	public TaskDefinition getTaskDefinition(String name) {
		for (TaskDefinition definition : getTaskDefinitions()) {
			if (definition.getName().equals(name)) {
				return definition;
			}
		}
		
		return null;
	}
}
